﻿/*
 * Created by SharpDevelop.
 * User: ivo.stoykov
 * Date: 3.12.2013 г.
 * Time: 11:38
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using HtmlAgilityPack;
using System.Text.RegularExpressions;

namespace DictNS
{
  /// <summary>
  /// Description of Parsers.
  /// </summary>
  public class Parsers
  {
    public Boolean isNewWord {get;set;}
//    string SoughtWord { get;set; }
//    string SoughtUrl {  get; set; }
//    private dynamic o = new ExpandoObject();
    private Word o = new Word();

    public Parsers()    
    {
      this.isNewWord = true;
//      this.o.mp3Url = String.Empty;
//      this.o.partOfSpeech = String.Empty;
//      this.o.error = String.Empty;
//      this.o.newUrl = null;
//      this.o.error =  null;
    }
    public Parsers(Boolean isNew) { this.isNewWord = isNew; }

    public Word doParseHtml(string html, string word, string selectedText)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      String host = String.Empty;
      this.o.SoughtWord = word;
      this.o.SoughtUrl = selectedText.Replace("%s", word.Replace(' ', '-'));
      try {
        Uri uri = new Uri(selectedText.ToLower());
        host = uri.Host.ToLower();
      } catch(Exception) {  host = String.Empty;  }
      switch (host) {
        case "www.macmillandictionary.com":
          return ParseMacmillan(ref html);
        case "dictionary.cambridge.org":
          return ParseCambridge(ref html);
        case "www.oxforddictionaries.com":
           return ParseOxford(ref html);
        default:
            (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
            return this.o;
//          throw new ApplicationException(String.Format("{0} - Unknown search engine . . ."));
      }
    }
    
    private Word ParseMacmillan(ref String html)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      try {
        HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);
        HtmlNode headbar = doc.DocumentNode.SelectSingleNode("//div[@class='headbar']");
        if(headbar == null) {  headbar = doc.DocumentNode.SelectSingleNode("//div[@id='headbar']");  }
        if(headbar == null) {  headbar = doc.DocumentNode.SelectSingleNode("//body");  }
        foreach (HtmlNode img in headbar.SelectNodes(".//img"))
        {
          if(!img.HasAttributes) { continue; }
          if(img.Attributes["data-src-mp3"] == null) { continue; }
          if(img.Attributes["data-src-mp3"].Value.Contains(Regex.Replace(this.o.SoughtWord.ToString().ToLower(), @"[\-_]", "_"))) 
          {
            this.o.Mp3Url = img.Attributes["data-src-mp3"].Value;
            break;
          }
        }
        HtmlNode node = headbar.SelectSingleNode(".//span[@class='PART-OF-SPEECH']");
        if(node != null ) {  this.o.PartOfSpeech = node.InnerText;  }
        if(this.isNewWord)  { this.o.NewUrl = ShowMacmillanRelatedEntries(ref doc);  }
      } catch (Exception ex) {
        this.o.Error = String.Format("{0} - {1}", ex.Message, (ex.InnerException != null ? ex.InnerException.Message : String.Empty));
        return this.o;
      }      
      return this.o;
    }
    
    private String ShowMacmillanRelatedEntries(ref HtmlAgilityPack.HtmlDocument doc)
    {
#if DEBUG
(new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
#endif
      HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//*[@id='relatedentries']//a");
      String newUrl = String.Empty;
      if(nodes == null) { return newUrl; }
      EntryList eList = new EntryList();
      eList.WordList.Add(this.o.SoughtUrl, String.Format("{0} {1}", this.o.SoughtWord,this.o.PartOfSpeech));
      foreach (HtmlNode el in nodes) {
        if(eList.WordList.ContainsKey(el.Attributes["href"].Value))  {  continue;  }
        if(el.Attributes["href"] == null || el.Attributes["title"] == null)  {  continue;  }
        eList.WordList.Add(el.Attributes["href"].Value, el.Attributes["title"].Value);
      }
      if(eList.ShowDialog() == DialogResult.OK) 
      {
        KeyValuePair<String, String> retVal = eList.ChosenWord;
        if(!String.IsNullOrEmpty(retVal.Key) && !String.IsNullOrEmpty(retVal.Value))
        {
          if(!retVal.Value.Equals(eList.WordList.Values.First())) {  newUrl = retVal.Key;  }
        }
      }
      eList.Dispose();
      return newUrl;
    }
    
    private Word ParseCambridge(ref String html)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      try {
        HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);
        HtmlNode node = doc.DocumentNode.SelectSingleNode("//span[@class='posgram']"); 
        if(node != null ) {  this.o.PartOfSpeech = node.InnerText;  }
        node = doc.DocumentNode.SelectSingleNode(String.Format("//span[contains(@title, '{0}')]", this.o.SoughtWord.ToString().ToLower()));
        if(node != null ) {  this.o.Mp3Url = node.Attributes["data-src-mp3"].Value;  }
        
        if(this.isNewWord)  { this.o.NewUrl = ShowCambridgeRelatdEntries(ref doc);  }
      } catch (Exception ex) {
        this.o.Error = String.Format("{0} - {1}", ex.Message, (ex.InnerException != null ? ex.InnerException.Message : String.Empty));
        return this.o;
      }      
      return this.o;
    }
    
    private String ShowCambridgeRelatdEntries(ref HtmlAgilityPack.HtmlDocument doc)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//div[@id='more-results']//ul[@class='result-list']//a");
      String newUrl = String.Empty;
      if(nodes == null) { return newUrl; }
      EntryList eList = new EntryList();
      eList.WordList.Add(this.o.SoughtUrl, String.Format("{0} {1}", this.o.SoughtWord,this.o.PartOfSpeech));
      foreach (HtmlNode el in nodes) {
        if(eList.WordList.ContainsKey(el.Attributes["href"].Value))  {  continue;  }
        if(el.Attributes["href"] == null)  {  continue;  }
        eList.WordList.Add(el.Attributes["href"].Value, el.InnerText);
      }
      if(eList.ShowDialog() == DialogResult.OK) 
      {
        KeyValuePair<String, String> retVal = eList.ChosenWord;
        if(!String.IsNullOrEmpty(retVal.Key) && !String.IsNullOrEmpty(retVal.Value))
        {
          if(!retVal.Value.Equals(eList.WordList.Values.First())) {  newUrl = retVal.Key;  }
        }
      }
      eList.Dispose();
      return newUrl;
    }

    private int ShowOxfordRelatedEntries(ref HtmlAgilityPack.HtmlDocument doc)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      int res = -1;
      HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//*[contains(@class, 'senseGroup')]/*[contains(@class, 'partOfSpeech')]");
      if(nodes == null) { return 0; }
      EntryList eList = new EntryList();
      foreach (HtmlNode el in nodes) {
        if(String.IsNullOrEmpty(el.InnerText.Trim()))  {  continue;  }
        if(eList.WordList.ContainsKey(eList.WordList.Count.ToString()))  {  continue;  }
        eList.WordList.Add(eList.WordList.Count.ToString(), String.Format("{0} {1}", this.o.SoughtWord, el.InnerText.Trim()));
      }
      try {  res = eList.ShowDialog() == DialogResult.OK ? Convert.ToInt32(eList.ChosenWord.Key.Trim()) : -1;  } 
      catch(Exception) {  res = -1; }
      eList.Dispose();
      return res;
    }

    private Word ParseOxford(ref String html)
    {
      #if DEBUG
      (new dict()).logIt(String.Format("method [{0}] fired", (new StackTrace(true).GetFrame(0).GetMethod().Name)));
      #endif
      try {
        int pos = 0;
        HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);
        HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//*[contains(@class, 'senseGroup')]/*[contains(@class, 'partOfSpeech')]");        
        if(nodes.Count == 0) {  return this.o;  }
        HtmlNodeCollection SoundNodes = doc.DocumentNode.SelectNodes("//*[contains(@class, 'sound')]"); 
        if(SoundNodes.Count == 0) {  return this.o;  }
        this.o.PartOfSpeech = nodes[pos].InnerText.Trim();
        if(nodes.Count > 1) {  pos = ShowOxfordRelatedEntries(ref doc);  }
        if(pos < 0) {  return this.o;  }  // nothing chosen
        this.o.Mp3Url = SoundNodes[pos].Attributes["data-src-mp3"].Value.Trim();
      } catch (Exception ex) {
        this.o.Error = String.Format("{0} - {1}", ex.Message, (ex.InnerException != null ? ex.InnerException.Message : String.Empty));
        return this.o;
      }      
      return this.o;
    }
  }
}
